package com.ygsoft.nio.server.filemanager;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;

import org.apache.log4j.Logger;

public class ServerCore extends Thread {
	private Logger log = Logger.getLogger(ServerCore.class);
	
	private int port = 888;
	private ServerSocketChannel serverChannel = null;
	private Selector selector = null;
	private InetAddress hostAddress = null;
	private ReadWorker readWorker = null;
	
	private volatile boolean isRunning = false;
	
	private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
	
	public ServerCore (InetAddress hostAddress, int port, ReadWorker readWorker) throws IOException {
		this.port = port;
		this.hostAddress = hostAddress;
		this.readWorker = readWorker;
		
		this.initServerSocket();
	}
	
	private void initServerSocket() throws IOException{
		this.selector = SelectorProvider.provider().openSelector();
		
		this.serverChannel = ServerSocketChannel.open();
		this.serverChannel.configureBlocking(false);
		
		InetSocketAddress isa = new InetSocketAddress(this.hostAddress, this.port);
		this.serverChannel.socket().bind(isa);
		
		this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
	}
	
	public void run(){
		System.out.println("Active Accept Thread ..");
		int selectedKey = -1;
		
		while(this.isRunning){
			try {
				log.debug("Wait New Keys..");
				selectedKey = this.selector.select();
				log.debug("Selected Key :" + selectedKey);
				
				Iterator<SelectionKey> selKeys = this.selector.selectedKeys().iterator();
				while(selKeys.hasNext()){
					SelectionKey key = selKeys.next();
					selKeys.remove();
					if(!key.isValid()) continue;
					
					if(key.isAcceptable()){
						log.debug("Detect Accepted Key ..");
						this.accept(key);
					} else if(key.isReadable()){
						log.debug("Detect READ Key ..");
						this.read(key);
					} else if(key.isWritable()){
						log.debug("Detect WRITE Key ..");
						this.write(key);
					}
					
				}
				
				
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}
	}
	
	private void accept(SelectionKey key) throws IOException {
		ServerSocketChannel serverSockChannel = (ServerSocketChannel) key.channel();
		
		SocketChannel socketChannel = serverSockChannel.accept();
		socketChannel.configureBlocking(false);
		
		socketChannel.register(this.selector, SelectionKey.OP_READ);
		
	}
	
	private void read(SelectionKey key) throws IOException {

		SocketChannel socketChannel = (SocketChannel) key.channel();
		this.readBuffer.clear();
		
		int numRead;
		try {
			numRead = socketChannel.read(this.readBuffer);
			log.debug("Read Completed :" + numRead);
			
			if (numRead == -1) {
				log.debug("Socket Closed.. " + socketChannel.socket().getInetAddress().toString());
				// Remote entity shut the socket down cleanly. Do the
				// same from our end and cancel the channel.
				key.channel().close();
				key.cancel();
				return;
			}
			
			byte[] dataArray = this.readBuffer.array();
			byte[] dataClone = new byte[numRead];
			System.arraycopy(dataArray, 0, dataClone, 0, numRead);
			
			this.readWorker.addNewEvent(new ServerDataEvent(this, socketChannel, dataClone));
			
			//TODO impl send data
			
			
		} catch (IOException e) {
			// The remote forcibly closed the connection, cancel
			// the selection key and close the channel.
			key.cancel();
			socketChannel.close();
			return;
		}
		
		
		
		// process data
		

	}
	
	private void write(SelectionKey key) throws IOException {
		// write file data
	}
	
	
	public synchronized void startServer(){
		this.isRunning = true;
		this.start();
	}
	
	public synchronized void stopServer(){
		this.isRunning = false;
	}
	
	public static void main(String ... v){
		System.out.println("Start Server ..");
		try {
			ReadWorker readWorker = new ReadWorker();
			Thread thrWorker = new Thread(readWorker);
			thrWorker.start();
			ServerCore test = new ServerCore(null, 9090, readWorker);
			test.startServer();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}
